#include <iostream>
#include <sstream>
#include "View3DPerspective.h"
#include "config.h"

View3DPerspective* View3DPerspective::instance = 0;
/*
 * callBack permettant d'appeler la fonction setRefreshView de View3DOrtho
 */
void canRefreshViewCallBackPers(int val) {
	View3DPerspective::getInstance()->setRefreshView(true);
}
View3DPerspective::View3DPerspective() :
		View3D() {
	refreshView = true;
}

void View3DPerspective::viewPortMiddle() { //view contenant le jeu
	//la viewport1 prend 2.5/5 de la fênetre
	glViewport(0, (h_global * 1.5) / 5, w_global, (h_global * 2.5) / 5); // Taille et positionnement de la viewport
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gererProjection();

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	gererLookAt();
	
	//initialisation des lumières
	initLights();
}

/*
 * définit si la caméra peut être déplacée ou non
 */
void View3DPerspective::setRefreshView(bool value) {
	refreshView = value;
}

void View3DPerspective::gererProjection() {
	/*
	 * initialisation du projection perspective
	 */
	gluPerspective(
			45.0,
			(model->getLabyrinthe()->getMatrixWidth() + 3.5)
					/ model->getLabyrinthe()->getMatrixHeight(), 0.1f, 1000);

}

void View3DPerspective::gererLookAt() {
	PacMan pacman = model->getPacMan();
	//position ortogonale pacman en X
	pacmanX = -(pacman.getCurrentPosX()
			- (model->getLabyrinthe()->getMatrixWidth() / 2.0));
	//position ortogonale pacman en Y
	pacmanY = pacman.getCurrentPosY()
			- (model->getLabyrinthe()->getMatrixHeight() / 2.0);
	if (!pacman.isIdle()) {
		applySmoothLookAt();
	}

	if (model->isModeFirstPerson()) {

		int ratioX = 0;
		int ratioY = 0;
		float distance = 3.5;
		if (pacman.getCurrentDirection() == RIGHTDIRECTION) {
			ratioX += -distance;
		} else if (pacman.getCurrentDirection() == LEFTDIRECTION
				|| pacman.getCurrentDirection() == NONEDIRECTION) {
			ratioX += +distance;
		} else if (pacman.getCurrentDirection() == UPDIRECTION) {
			ratioY += -distance;
		} else {
			ratioY += +distance;
		}
		gluLookAt(-pacmanX + ratioX, -pacmanY + ratioY, 2.0, -pacmanX - ratioX,
				-pacmanY - ratioY, 0.9, 0, 0, 1);

	} else {
		glRotated(model->getViewRotation(), 0, 0, 1);
		if (model->getZoomFactor() < 1.0) { //si il y a un zoom avant par rapport à la position neutral
			gluLookAt(-pacmanX, -pacmanY - 3, model->getZoomFactor() * 10.0,
					-pacmanX, -pacmanY, 0, 0, 1, 0);
		} else { //pas de zoom ou zoom arrière
			gluLookAt(
					0,
					-6,
					model->getLabyrinthe()->getMatrixHeight()
							* model->getZoomFactor(), 0, 1, 0, 0, 1, 0);
		}
	}
}

/*
 * Pattern singleton
 */
View3DPerspective *View3DPerspective::getInstance() {
	if (View3DPerspective::instance == 0)
		instance = new View3DPerspective();
	return instance;
}

/*
 *
 * Initalisation des lumières
 *
 */
void View3DPerspective::initLights() {

	enableLight();
	glEnable(GL_LIGHT2); // on allume/active la lumière 2
	glEnable(GL_LIGHT3);
	glDisable(GL_LIGHT1); //désactive la lumière1
	glDisable(GL_LIGHT0); //désactive la lumière0


	/*On initialise le spot sur la lumière 3*/
	glLightfv(GL_LIGHT3, GL_AMBIENT, light1_ambient);
	glLightfv(GL_LIGHT3, GL_DIFFUSE, light1_diffuse);
	glLightfv(GL_LIGHT3, GL_POSITION, light1_position);
	glLightfv(GL_LIGHT3, GL_SPECULAR, light1_specular);
	//spot direction
	glLightfv(GL_LIGHT3, GL_SPOT_DIRECTION, light1_direction);
	glLightf(GL_LIGHT3, GL_SPOT_CUTOFF, 45.0);

	glLightf(GL_LIGHT3, GL_CONSTANT_ATTENUATION, 0.02);
	glLightf(GL_LIGHT3, GL_LINEAR_ATTENUATION, 0.02);
	glLightf(GL_LIGHT3, GL_QUADRATIC_ATTENUATION, 0.02);
	glLightf(GL_LIGHT3, GL_SPOT_EXPONENT, 0.0);

	/*Lumière 2*/
	glLightfv(GL_LIGHT2, GL_AMBIENT, light2_ambient);
	glLightfv(GL_LIGHT2, GL_DIFFUSE, light2_diffuse);
	glLightfv(GL_LIGHT2, GL_POSITION, light2_position);
	glLightfv(GL_LIGHT2, GL_SPECULAR, light2_specular);

}
